package ha.web.server.manager;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


import ha.web.server.database.UserId;
import ha.web.server.database.UserStatistics;
import ha.web.server.exceptions.*;
public class GameRunning {

	private final String EOL = " \n";
	private final int TABLE_SIZE = 8;
	private static int gameCounterId = 53432;
	private UserId player1;
	private UserId player2;
	private int gameId;
	private String[][] prevTable;
	private String[][] table;
	private String chat;
	private boolean gameEnded;
	private boolean gameEndedForP1;
	private boolean gameEndedForP2;
	private UserStatistics statP1;
	private UserStatistics statP2;
	private String allMoves;
	private String lastMoveP1;
	private String lastMoveP2;
	private String timeStart;
	private String timeEnd;
	private Date start;


	//true for player1, false for player2
	private boolean playerTurn;
	private int playerTurnById;

	public GameRunning (UserId first, UserId sec) {
		gameEnded = false;
		player1 = first;
		player2 = sec;
		gameId = ++gameCounterId;
		playerTurn = true;
		playerTurnById = first.getUserID();
		table = new String[TABLE_SIZE][TABLE_SIZE];
		for(int i=0;i<TABLE_SIZE;i++) {
			for(int j=0;j<TABLE_SIZE;j++) {
				if(i<3) {
					if(i==0 && j<3)
						table[i][j] = "1";
					else if(i==1 && j<2)
						table[i][j] = "1";
					else if(i==2 && j<1)
						table[i][j] = "1";
					else
						table[i][j] = "0";
				} else if(i>4) {
					if(i==5 && j>6)
						table[i][j] = "2";
					else if(i==6 && j>5)
						table[i][j] = "2";
					else if(i==7 && j>4)
						table[i][j] = "2";
					else
						table[i][j] = "0";
				} else
					table[i][j] = "0";

			}
		}
		prevTable = table;
		chat = "";
		statP1 = new UserStatistics(player1.getUserID(),player1.getName());
		statP2 = new UserStatistics(player2.getUserID(),player2.getName());
		lastMoveP1="";
		lastMoveP2="";
		gameEndedForP1 = false;
		gameEndedForP2 = false;
		start = new Date();
		timeStart = new SimpleDateFormat ("hh:mm:ss").format(start);
		timeEnd = null;
		allMoves = "List Of all moves:<br>";
		

	}

	public UserId getPlayerTurn () {
		if(playerTurn)
			return player1;
		return player2;
	}

	public boolean itIsTurn (int user) {
		return playerTurnById==user;
	}

	public String getPlayerNameTurn () {
		return getPlayerTurn().getNickName();
	}

	public String getPlayerNameById(int userId) {
		if(player1.getUserID()==userId)
			return player1.getName();
		else return player2.getName();
	}

	public int getGameId() {
		return gameId;
	}

	public void switchTurn () {
		playerTurn = !playerTurn;
		playerTurnById = getPlayerTurn().getUserID();
	}

	public List<Integer> getPlayers() {
		List<Integer> toRet = new ArrayList<Integer>();
		toRet.add(player1.getUserID());
		toRet.add(player2.getUserID());
		return toRet;
	}

	public String getTable () {
		String toRet = "";
		for(int i=0;i<8;i++) {
			for(int j=0;j<8;j++) {
				toRet += (table[j][i]+":");
			}
		}
		return toRet;
	}
	
	public String getPrevTable () {
		String toRet = "";
		for(int i=0;i<8;i++) {
			for(int j=0;j<8;j++) {
				toRet += (prevTable[j][i]+":");
			}
		}
		return toRet;
	}
	
	public void setTable (String[][] table) {
		this.table= table;  
	}

	public String getChat() {
		return chat;
	}

	public void addLineToChat (int userId, String line) {
		if (line.equalsIgnoreCase("")) return;

		Date dNow = new Date();
	    SimpleDateFormat ft = new SimpleDateFormat ("hh:mm:ss");
		chat+= ft.format(dNow) + " ";
	    chat += getPlayerNameById(userId);
		chat += ": ";
		chat += line;
		chat += EOL;

	}

	public List<UserStatistics> setLastMove(int userId,String lastMove) throws NotYourTurnException, EmptyMoveException, 
	SyntaxMoveNotValidException, MoveNotValidException, MoveOutOfBoundException {
		String[][] tmpTable = validateMove(userId,lastMove);
		if(player1.getUserID()==userId) {
			lastMoveP1 =lastMove;
			allMoves += player1.getNickName()+": ";
		} else if(player2.getUserID()==userId) {
			lastMoveP2 =lastMove;
			allMoves += player2.getNickName()+": ";
		}
		allMoves += (lastMove+"<br>");
		prevTable = table;
		table = tmpTable;
		int gameEndId = checkEndGame();
		if(gameEndId>0) {
			gameEnded = true;
			if(gameEndId==player1.getUserID()) {
				statP1.setEndGame(true, false);
				statP2.setEndGame(false, true);
			}else {
				statP1.setEndGame(false, true);
				statP2.setEndGame(true, false);
			}
			return endGame(gameEndId,true,false);
		}
		switchTurn();
		return null;
	}
	
	private int checkEndGame() {
		if(table[7][5]=="1" && table[7][6]=="1" && table[7][7]=="1" 
				&& table[6][6]=="1" && table[6][7]=="1" && table[5][7]=="1")
			return player1.getUserID();
		else if (table[0][0]=="2" && table[0][1]=="2" && table[0][2]=="2" 
				&& table[1][0]=="2" && table[1][1]=="2" && table[2][0]=="2")
			return player2.getUserID();
		else return -1;
	}

	private String[][] validateMove(int userId, String lastMove) throws NotYourTurnException, EmptyMoveException, SyntaxMoveNotValidException, MoveNotValidException, MoveOutOfBoundException {
		boolean nextMovAllowed = true;
		String[] singleMov,moves,fromStr,toStr;
		String[][] tmpTable = table;
		int fromRow,fromCol,toRow,toCol;
		String playerLabel;
		if(getPlayerNameById(userId).equals(player1.getName()))
			playerLabel = "1";
		else 
			playerLabel = "2";
		//chk turn
		if(userId!=playerTurnById)
			throw new NotYourTurnException("Not Your Turn Erroe: Stop abuse the code");
		//chk emty move
		if(lastMove=="")
			throw new EmptyMoveException("Empty Move Error: You didnt move any of your pieces ");
		//chk correct syntax 
		if(!lastMove.contains(";"))
			throw new SyntaxMoveNotValidException("Syntax Error: Missing EOL");
		moves = lastMove.split(";");
		int numJump = 0;
		for(int i=0;i<moves.length;i++) {
			if(!moves[i].contains("->"))
				throw new SyntaxMoveNotValidException("Syntax Error: Missing -> at move number: "+i);
			singleMov = moves[i].split("->");
			if(singleMov.length!=2)
				throw new SyntaxMoveNotValidException("Syntax Error: -> not at a right pos at move number: "+i);
			if(!singleMov[0].contains("-") || !singleMov[1].contains("-"))
				throw new SyntaxMoveNotValidException("Syntax Error: Missing - at move number: "+i);
			fromStr = singleMov[0].split("-");
			toStr = singleMov[1].split("-");
			//chk numbers are parsable
			try {
				fromRow = Integer.parseInt(fromStr[0]);
				fromCol = Integer.parseInt(fromStr[1]);
				toRow = Integer.parseInt(toStr[0]);
				toCol = Integer.parseInt(toStr[1]);
			} catch (Exception e) {
				throw new SyntaxMoveNotValidException("Syntax Error: Move nt contain parsable integer at move number: "+i);
			}
			//chk number at board bound
			chkBound(fromRow);
			chkBound(fromCol);
			chkBound(toRow);
			chkBound(toCol);
			//chk valid of from
			if(!tmpTable[fromCol][fromRow].equals(playerLabel))
				throw new MoveNotValidException("Move Validation Error: Move from square "+fromCol+"-"+fromRow+" not allowed");
			//chk valid of to 
			if(!tmpTable[toCol][toRow].equals("0"))
				throw new MoveNotValidException("Move Validation Error: Move to square "+fromCol+"-"+fromRow+" not allowed");
			//chk if allowed to play 
			if(!nextMovAllowed)
				throw new MoveNotValidException("Move Validation Error: Cannot do move number: "+i);
			//chk moves direction
			boolean isJamp = false;
			boolean isVertical = false;
			if(fromRow==toRow)
				isVertical = true;
			if(fromCol==toCol && isVertical)
				throw new MoveNotValidException("Move Validation Error: Cannot stay on same square (at move number: "+i+")");
			if(Math.abs(fromRow-toRow)>2 ||Math.abs(fromCol-toCol)>2 )
				throw new MoveNotValidException("Move Validation Error: Cannot make this move (at move number: "+i+")");
			if(Math.abs(fromRow-toRow)==2 || Math.abs(fromCol-toCol)==2)
				isJamp = true;
			//case single step
			if(!isJamp) 
				nextMovAllowed = false;
			//case jump
			if(isJamp && tmpTable[toCol==fromCol?toCol:(toCol-fromCol)<0?fromCol-1:fromCol+1]
					[toRow==fromRow?toRow:(toRow-fromRow)<0?fromRow-1:fromRow+1]=="0")
				throw new MoveNotValidException("Move Validation Error: Must jump above another piece (at move number: "+i+")");
			if(isJamp)
				numJump++;
			//mark chg
			tmpTable[fromCol][fromRow] = "0"; 
			tmpTable[toCol][toRow] = playerLabel;
		}
		updateSta(userId,numJump);
		return tmpTable;

	}

	private void updateSta(int userId, int numJump) {
		if(player1.getUserID()==userId)
			statP1.update(numJump);
		else
			statP2.update(numJump);

	}

	private void chkBound(int num) throws MoveOutOfBoundException {
		if(num<0 || num>(TABLE_SIZE-1))
			throw new MoveOutOfBoundException("Out Of Bound Error: Move to/from "+num+" not in the board");
	}

	public String getLastMove(int userId) {
		if(player1.getUserID()==userId)
			return lastMoveP2;
		else 
			return lastMoveP1;
	}

	

	public List<UserStatistics> endGame(int userId, boolean won, boolean resigned) {
		List<UserStatistics>  toRet = new ArrayList<UserStatistics>();
		gameEnded = true;
		setGameEndedForPlayer(userId);
		if(resigned) {
			if(userId==player1.getUserID()) {
				statP1.setEndGame(false, true);
				statP2.setEndGame(true, false);
			}else {
				statP1.setEndGame(true, false);
				statP2.setEndGame(false, true);
			}
		}
		timeEnd = new SimpleDateFormat ("hh:mm:ss").format(new Date());
		int totalMove = statP1.getNumberOfMove()+ statP2.getNumberOfMove();
		statP1.setEndGameData(timeStart, timeEnd,totalMove,allMoves,player2.getNickName(),statP2.getBigestJump());
		statP2.setEndGameData(timeStart, timeEnd,totalMove,allMoves,player1.getNickName(),statP1.getBigestJump());
		toRet.add(statP1);
		toRet.add(statP2);
		return toRet;
	}

	public UserId getOtherPlayer(int userId) {
		if(player1.getUserID()==userId)
			return player2;
		else 
			return player1;
	}
	
	public boolean gameEnded() {
		return gameEnded;
	}
	
	public boolean isGameEndedForBoth() {
		return (gameEndedForP1&&gameEndedForP2);
	}
		
	
	public boolean isGameEndedForP1() {
		return gameEndedForP1;
	}

	public boolean isGameEndedForP2() {
		return gameEndedForP2;
	}

	public void setGameEndedForPlayer(int userId) {
		if(player1.getUserID()==userId)
			this.gameEndedForP1 = true;
		else 
			this.gameEndedForP2 = false;
	}

	public String getGameHistory(int userId) {
		if(player1.getUserID()==userId)
			return statP1.toString();
		else 
			return statP2.toString();
	}

	public String getStatistics(int userId) {
		if(player1.getUserID()==userId)
			return statP1.getTmpStatistics(player2.getNickName(),
					statP2.getBigestJump(), statP1.getNumberOfMove()+ statP2.getNumberOfMove());
		else 
			return statP1.getTmpStatistics(player1.getNickName(),
					statP1.getBigestJump(), statP1.getNumberOfMove()+ statP2.getNumberOfMove());
	}


}
